a tool for shared writing and social publishing
at main 3.4 kB view raw
1import { z } from "zod"; 2import { 3 PullRequest, 4 PullResponseV1, 5 VersionNotSupportedResponse, 6} from "replicache"; 7import type { Fact } from "src/replicache"; 8import { FactWithIndexes } from "src/replicache/utils"; 9import type { Attribute } from "src/replicache/attributes"; 10import { makeRoute } from "../lib"; 11import type { Env } from "./route"; 12 13// First define the sub-types for V0 and V1 requests 14const pullRequestV0 = z.object({ 15 pullVersion: z.literal(0), 16 schemaVersion: z.string(), 17 profileID: z.string(), 18 cookie: z.any(), // ReadonlyJSONValue 19 clientID: z.string(), 20 lastMutationID: z.number(), 21}); 22 23// For the Cookie type used in V1 24const cookieType = z.union([ 25 z.null(), 26 z.string(), 27 z.number(), 28 z 29 .object({ 30 order: z.union([z.string(), z.number()]), 31 }) 32 .and(z.record(z.string(), z.any())), // ReadonlyJSONValue with order property 33]); 34 35const pullRequestV1 = z.object({ 36 pullVersion: z.literal(1), 37 schemaVersion: z.string(), 38 profileID: z.string(), 39 cookie: cookieType, 40 clientGroupID: z.string(), 41}); 42 43// Combined PullRequest type 44const PullRequestSchema = z.union([pullRequestV0, pullRequestV1]); 45 46export const pull = makeRoute({ 47 route: "pull", 48 input: z.object({ pullRequest: PullRequestSchema, token_id: z.string() }), 49 handler: async ({ pullRequest, token_id }, { supabase }: Env) => { 50 let body = pullRequest; 51 if (body.pullVersion === 0) return versionNotSupported; 52 let { data, error } = await supabase.rpc("pull_data", { 53 token_id, 54 client_group_id: body.clientGroupID, 55 }); 56 if (!data) { 57 console.log(error); 58 59 return { 60 error: "ClientStateNotFound", 61 } as const; 62 } 63 64 let facts = data.facts as { 65 attribute: string; 66 created_at: string; 67 data: any; 68 entity: string; 69 id: string; 70 updated_at: string | null; 71 version: number; 72 }[]; 73 let publication_data = data.publications as { 74 description: string; 75 title: string; 76 tags: string[]; 77 }[]; 78 let pub_patch = publication_data?.[0] 79 ? [ 80 { 81 op: "put", 82 key: "publication_description", 83 value: publication_data[0].description, 84 }, 85 { 86 op: "put", 87 key: "publication_title", 88 value: publication_data[0].title, 89 }, 90 { 91 op: "put", 92 key: "publication_tags", 93 value: publication_data[0].tags || [], 94 }, 95 ] 96 : []; 97 98 let clientGroup = ( 99 (data.client_groups as { 100 client_id: string; 101 client_group: string; 102 last_mutation: number; 103 }[]) || [] 104 ).reduce( 105 (acc, clientRecord) => { 106 acc[clientRecord.client_id] = clientRecord.last_mutation; 107 return acc; 108 }, 109 {} as { [clientID: string]: number }, 110 ); 111 112 return { 113 cookie: Date.now(), 114 lastMutationIDChanges: clientGroup, 115 patch: [ 116 { op: "clear" }, 117 { op: "put", key: "initialized", value: true }, 118 ...(facts || []).map((f) => { 119 return { 120 op: "put", 121 key: f.id, 122 value: FactWithIndexes(f as unknown as Fact<Attribute>), 123 } as const; 124 }), 125 ...pub_patch, 126 ], 127 } as PullResponseV1; 128 }, 129}); 130 131const versionNotSupported: VersionNotSupportedResponse = { 132 error: "VersionNotSupported", 133 versionType: "pull", 134};